// Internal action code for project skygarden

package math;

import jason.asSemantics.DefaultInternalAction;
import jason.asSemantics.TransitionSystem;
import jason.asSemantics.Unifier;
import jason.asSyntax.Atom;
import jason.asSyntax.NumberTerm;
import jason.asSyntax.Term;
import jason.environment.grid.Location;

import java.util.ArrayList;
import java.util.List;

import env.GardenModel;
import busca.AEstrela;
import busca.Busca; // busca : search
import busca.Estado; // estado : state
import busca.Heuristica; // Heuristic
import busca.Nodo; // node

@SuppressWarnings("serial")
public class get_direction extends DefaultInternalAction {

	GardenModel model = GardenModel.get();

	@Override
	public Object execute(TransitionSystem ts, Unifier un, Term[] args)
			throws Exception {
		try {
			String sAction = "skip";
			int numcheck = (int) ((NumberTerm) args[0]).solve();
			int iagx = (int) ((NumberTerm) args[1]).solve();
			int iagy = (int) ((NumberTerm) args[2]).solve();
			int itox = (int) ((NumberTerm) args[3]).solve();
			int itoy = (int) ((NumberTerm) args[4]).solve();

			if (model.inGrid(itox, itoy)) {
				Busca searchAlg = new AEstrela();
				// searchAlg.setMaxAbertos(1000);
				Location lini = new Location(iagx, iagy);
				// destination should be a free place
				while (!model.isFreeOfObstacle(itox, itoy) && itox > 0)
					itox--;
				while (!model.isFreeOfObstacle(itox, itoy) && itox < model.getWidth())
					itox++;

				Nodo solution = searchAlg.busca(new GridState(numcheck,lini, lini,
						new Location(itox, itoy), model, "initial"));
				if (solution != null) {
					// System.out.println(iagx+"-"+iagy+"/"+itox+"-"+itoy+" = "+solution.montaCaminho());
					Nodo root = solution;
					Estado prev1 = null;
					Estado prev2 = null;
					while (root != null) {
						prev2 = prev1;
						prev1 = root.getEstado();
						root = root.getPai();
					}
					if (prev2 != null) {
						sAction = ((GridState) prev2).op;
					}
				} else {
					// System.out.println("No route from "+iagx+"x"+iagy+" to "+itox+"x"+itoy+"!");
				}
			}
			return un.unifies(args[5], new Atom(sAction));
		} catch (Throwable e) {
			e.printStackTrace();
			return false;
		}
	}
}

class GridState implements Estado, Heuristica {

	// State information
	Location pos; // current location
	Location from, to;
	String op;
	int numcheck;
	GardenModel model;
	int[][] array ;

	public GridState(int numcheck,Location l, Location from, Location to, GardenModel model,
			String op) {
		this.array = model.Array();
		this.numcheck = numcheck;
		this.pos = l;
		this.from = from;
		this.to = to;
		this.model = model;
		this.op = op;
	}

	public int custo() {
		return 1;
	}

	public boolean ehMeta() {
		return pos.equals(to);
	}

	public String getDescricao() {
		return "Grid search";
	}

	public int h() {
		return pos.distance(to);
	}

	public List<Estado> sucessores() {
		List<Estado> s = new ArrayList<Estado>(8);
		// four directions
		suc(s, new Location(pos.x - 1, pos.y), "left");
		suc(s, new Location(pos.x + 1, pos.y), "right");
		suc(s, new Location(pos.x, pos.y - 1), "up");
		suc(s, new Location(pos.x, pos.y + 1), "down");
		suc(s, new Location(pos.x - 1, pos.y - 1), "up_left");
		suc(s, new Location(pos.x + 1, pos.y - 1), "up_right");
		suc(s, new Location(pos.x - 1, pos.y + 1), "down_left");
		suc(s, new Location(pos.x + 1, pos.y + 1), "down_right");	
		
		return s;
	}

	private void suc(List<Estado> s, Location newl, String op) {
		if ((model.isFree(newl) && ((model.isFree(GardenModel.GREEN, newl) && model.isFree(GardenModel.RED, newl))
				|| (!model.isFree(GardenModel.RED, newl) && numcheck == 1) || !model.isFree(GardenModel.GRAY, newl)))
				|| (from.distance(newl) > 3 && model.isFreeOfObstacle(newl))) {
			s.add(new GridState(this.numcheck,newl, from, to, model, op));
		}
	}

	public boolean equals(Object o) {
		try {
			GridState m = (GridState) o;
			return pos.equals(m.pos);
		} catch (Exception e) {
		}
		return false;
	}

	public int hashCode() {
		return pos.hashCode();
	}

	public String toString() {
		return "(" + pos + "-" + op + ")";
	}
}
